package edu.hawaii.webspider;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import org.junit.Test; 
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.LogRecord;

/**
 * Test cases for the WebSpider class.
 *
 * @author Randy Cox
 */
public class TestWebSpider {
  
  /**
   * Test URL pointing to a dedicated site with known fixed statistics.  
   */
  public static final String TEST_URL 
    = "http://www2.hawaii.edu/~randycox/11.WebSpider/test/main.htm";

  /**
   * Test URL pointing to a non-existent link.
   */
  public static final String TEST_URL_ERROR 
    = "http://www2.hawaii.edu/~randycox/11.WebSpider/test/error.htm";
 
  /**
   * Test value used for Max Page level.
   */
  public static final Integer TEST_TOTAL_LINKS = 124;

  /**
   * Test value used for Max Page level.
   */
  public static final Integer TEST_ERROR_LINKS = 0;

  /**
   * Test value used for Max Page level.
   */
  public static final String TEST_MAX_PAGES = "99";
  
  /**
   * Test link of resulting most popular URL from test site.
   */
  public static final String TEST_POPULAR_URL 
    = "http://www2.hawaii.edu/~randycox/11.WebSpider/test/002-008.htm";
  
  /**
   * Amount of links point to most popular ULR in test site.
   */
  public static final Integer TEST_POPULAR_URL_LINK_COUNT = 12;
  
  /**
   * Colon String.
   */
  String colonStr = ": ";
  
  /**
   * Equal String.
   */
  String equalStr = " = ";
  
  /**
   * Comma string.
   */
  String commaStr = ", ";

  /**
   * Equal true string.
   */
  String trueStr = " = true";
  
  /**
   * Equal false string.
   */
  String falseStr = " = false";
  
  /**
   * Equal true with command string.
   */
  String trueComStr = " = true, ";
  
  /**
   * Equal False with comma string.
   */
  String falseComStr = " = false, ";
  
  /**
   * Test str.
   */
  String testStr = "Test";
  
  /**
   * Creates the Webspider class and runs it with passed in paramters.
   * 
   * @param parameters Argument list to be parsed.
   * @return String Result of statistics collection from program
   * @throws Exception when non-reachable URL is used
   */
  public String runWebSpider(String[] parameters) throws Exception {
    WebSpider webSpider = new WebSpider(parameters);
    return webSpider.run();
  }
  
  /**
   *  Test with full parameter list and verifies that all internal variables are set correctly.
   *  
   * @throws Exception when non-reachable URL is used
   */
  @Test
  public void testParseParamsAll() throws Exception {
 
    String[] testParameters = { 
      TEST_URL, 
      WebSpider.PARAM_HELP, 
      WebSpider.PARAM_TEST_PARSE,
      WebSpider.PARAM_TOTAL_LINKS,    
      WebSpider.PARAM_MOST_POPULAR,
      WebSpider.PARAM_LOGGING,
      TEST_MAX_PAGES
    };
    String outputString = this.runWebSpider(testParameters);

    String expectedResult 
      = WebSpider.RESULT_TAG_LIST[0] + colonStr // Title
      + WebSpider.RESULT_TAG_LIST[1] + trueComStr // Help On
      + WebSpider.RESULT_TAG_LIST[2] + trueComStr // Test Parse On
      + WebSpider.RESULT_TAG_LIST[3] + trueComStr // Total Links On
      + WebSpider.RESULT_TAG_LIST[4] + trueComStr // Most Popular On
      + WebSpider.RESULT_TAG_LIST[5] + trueComStr // Logging On
      + WebSpider.RESULT_TAG_LIST[6] + trueComStr // Max Page OK
      + WebSpider.RESULT_TAG_LIST[7] + trueComStr // URL OK
      + WebSpider.RESULT_TAG_LIST[8] + equalStr + TEST_URL + ", "
      + WebSpider.RESULT_TAG_LIST[9] + equalStr + TEST_MAX_PAGES + ", " 
      + WebSpider.RESULT_TAG_LIST[10] + trueStr; 
    assertEquals("Test parser with all params.", expectedResult, outputString);
  }

  /**
   *  Test with no parameters and verifies the resulting string.
   *  
   * @throws Exception when non-reachable URL is used
   */
  @Test
  public void testParseParamsNone() throws Exception {

    String[] testParameters = { TEST_URL, WebSpider.PARAM_TEST_PARSE};
    String outputString = this.runWebSpider(testParameters);

    String expectedResult 
      = WebSpider.RESULT_TAG_LIST[0] + colonStr // Title
      + WebSpider.RESULT_TAG_LIST[1] + falseComStr // Help On
      + WebSpider.RESULT_TAG_LIST[2] + trueComStr // Test Parse On
      + WebSpider.RESULT_TAG_LIST[3] + falseComStr // Total Links On
      + WebSpider.RESULT_TAG_LIST[4] + falseComStr // Most Popular On
      + WebSpider.RESULT_TAG_LIST[5] + falseComStr  // Logging On
      + WebSpider.RESULT_TAG_LIST[6] + trueComStr  // Max Page OK
      + WebSpider.RESULT_TAG_LIST[7] + trueComStr  // URL OK
      + WebSpider.RESULT_TAG_LIST[8] + equalStr + TEST_URL + commaStr
      + WebSpider.RESULT_TAG_LIST[9] + equalStr + WebSpider.MAX_PAGE_DEFAULT + commaStr 
      + WebSpider.RESULT_TAG_LIST[10] + trueStr;  // Ok
    assertEquals("Test parser with no params.", expectedResult, outputString);
  }

  /**
   *  Test with different max page too large.
   *  
   * @throws Exception when non-reachable URL is used
   */
  @Test
  public void testParseParamsMaxPagesOver() throws Exception {
    
    String[] testParameters = { 
      TEST_URL,
      WebSpider.PARAM_TEST_PARSE,
      ((Integer)(WebSpider.MAX_PAGE_LIMIT + 1)).toString()
    };
    String outputString = this.runWebSpider(testParameters);
    
    String expectedResult 
      = WebSpider.RESULT_TAG_LIST[0] + colonStr // Title
      + WebSpider.RESULT_TAG_LIST[1] + falseComStr // Help On
      + WebSpider.RESULT_TAG_LIST[2] + trueComStr // Test Parse On
      + WebSpider.RESULT_TAG_LIST[3] + falseComStr // Total Links On
      + WebSpider.RESULT_TAG_LIST[4] + falseComStr // Most Popular On
      + WebSpider.RESULT_TAG_LIST[5] + falseComStr  // Logging On
      + WebSpider.RESULT_TAG_LIST[6] + falseComStr  // Max Page OK
      + WebSpider.RESULT_TAG_LIST[7] + trueComStr  // URL OK
      + WebSpider.RESULT_TAG_LIST[8] + equalStr + TEST_URL + commaStr
      + WebSpider.RESULT_TAG_LIST[9] + equalStr + testParameters[2] + commaStr 
      + WebSpider.RESULT_TAG_LIST[10] + falseStr;  // Ok
    assertEquals("Test parser with too high max pages.", expectedResult, outputString);
  }

  /**
   *  Tests the main function with max page of zero.
   *  
   * @throws Exception when non-reachable URL is used
   */
  @Test
  public void testParseParamsMaxPagesZero() throws Exception {
    
    String[] testParameters = { TEST_URL, WebSpider.PARAM_TEST_PARSE, ((Integer)0).toString()};
    String outputString = this.runWebSpider(testParameters);

    String expectedResult 
      = WebSpider.RESULT_TAG_LIST[0] + colonStr // Title
      + WebSpider.RESULT_TAG_LIST[1] + falseComStr // Help On
      + WebSpider.RESULT_TAG_LIST[2] + trueComStr // Test Parse On
      + WebSpider.RESULT_TAG_LIST[3] + falseComStr // Total Links On
      + WebSpider.RESULT_TAG_LIST[4] + falseComStr // Most Popular On
      + WebSpider.RESULT_TAG_LIST[5] + falseComStr  // Logging On
      + WebSpider.RESULT_TAG_LIST[6] + falseComStr  // Max Page OK
      + WebSpider.RESULT_TAG_LIST[7] + trueComStr  // URL OK
      + WebSpider.RESULT_TAG_LIST[8] + equalStr + TEST_URL + commaStr
      + WebSpider.RESULT_TAG_LIST[9] + equalStr + testParameters[2] + commaStr 
      + WebSpider.RESULT_TAG_LIST[10] + falseStr;  // Ok
    assertEquals("Test parser with too high max pages.", expectedResult, outputString);
  }

  /**
   * Test -help string.
   * 
   * @throws Exception when non-reachable URL is used
   */
  @Test
  public void testHelp() throws Exception {

    String[] testParameters = { TEST_URL, WebSpider.PARAM_HELP };
    String outputString = this.runWebSpider(testParameters);
    
    String expectedResult = WebSpider.HELP_STR;
    assertEquals("Test -help.", expectedResult, outputString);
  }
  
  /**
   * Test the normal operation of the getNumLinks() method.
   * 
   * @exception Exception If problem occurs during retrieval of the startUrl
   */
  @Test
  public void testTotalLinks() throws Exception {
    
    String[] testParameters = { TEST_URL, WebSpider.PARAM_TOTAL_LINKS, WebSpider.PARAM_LOGGING };
    String outputString = this.runWebSpider(testParameters);

    Object[] parameters = { WebSpider.MAX_PAGE_DEFAULT, TEST_URL, TEST_TOTAL_LINKS };
    String testResult = String.format(WebSpider.TOTAL_LINKS_STR, parameters); 
    assertEquals("Test request for total links.", testResult, outputString);
  }
  
  /**
   * Test the normal operation of the getMostPopular() method.
   * 
   * @exception Exception If problem occurs during retrieval of the startUrl
   */
  @Test
  public void testMostPopular() throws Exception {
    
    String[] testParameters = { TEST_URL, WebSpider.PARAM_MOST_POPULAR };
    String outputString = this.runWebSpider(testParameters);

    Object[] parameters = { TEST_POPULAR_URL, TEST_POPULAR_URL_LINK_COUNT };
    String testResult = String.format(WebSpider.MOST_POPULAR_STR, parameters); 
    assertEquals("Test request for total links.", testResult, outputString);
  }

  /**
   * Test getTotalLinks and getMostPopular() at the same time.
   * 
   * @exception Exception If problem occurs during retrieval of the startUrl
   */
  @Test
  public void testTotalLinksAndMostPopular() throws Exception {
    
    String[] testParams = { TEST_URL, WebSpider.PARAM_TOTAL_LINKS, WebSpider.PARAM_MOST_POPULAR };
    String outputString = this.runWebSpider(testParams);

    Object[] parameters1 = { WebSpider.MAX_PAGE_DEFAULT, TEST_URL, TEST_TOTAL_LINKS };
    String testResult1 = String.format(WebSpider.TOTAL_LINKS_STR, parameters1); 
    
    Object[] parameters2 = { TEST_POPULAR_URL, TEST_POPULAR_URL_LINK_COUNT };
    String testResult2 = String.format(WebSpider.MOST_POPULAR_STR, parameters2); 
    
    String testResult = testResult1.concat("\r\n" + testResult2);
    assertEquals("Test request for total links and most popular.", testResult, outputString);
  }
  
  /**
   * Test error condition where URL is bad.
   * 
   * @exception Exception If problem occurs during retrieval of the startUrl
   */
  @Test
  public void testRunWithBadURL() throws Exception {
    String[] testParameters = { TEST_URL_ERROR, WebSpider.PARAM_TOTAL_LINKS, 
        WebSpider.PARAM_LOGGING };
    String outputString = this.runWebSpider(testParameters);
    Object[] parameters = { WebSpider.MAX_PAGE_DEFAULT, TEST_URL_ERROR, TEST_ERROR_LINKS };

    String testResult = String.format(WebSpider.TOTAL_LINKS_STR, parameters); 
    assertEquals("Test error string.", testResult, outputString);
  }
  
  /**
   * Test execution of main function.
   * 
   * @exception Exception If problem occurs during retrieval of the startUrl
   */
  @Test  
  public void testMain() throws Exception {
    String[] testParameters = { TEST_URL, WebSpider.PARAM_TOTAL_LINKS };
    WebSpider.main(testParameters);
    assertTrue("Check if parsing successful",WebSpider.webSpider.parser.isOk);
  }

  /**
   * Test UrlItem stores data correctly. 
   */
  @Test
  public void testUrlItem() {
    WebSpider.UrlItem urlItem1 = new WebSpider.UrlItem(testStr);
    WebSpider.UrlItem urlItem2 = new WebSpider.UrlItem(testStr);
    urlItem1.count = 1;
    urlItem1.count = 2;
    assertEquals("Check compare", urlItem1, urlItem2);
    int hashCode = testStr.hashCode();
    assertEquals("Has code check", urlItem1.hashCode(), hashCode);
  }

  /**
   * Test UrlItem null error condition. 
   */
  @Test
  public void testUrlItemNull() {
    WebSpider.UrlItem urlItem = new WebSpider.UrlItem(testStr);
    Object nullObj = null;
    Boolean result = urlItem.equals(nullObj);
    assertFalse("Check compare with null", result);
  }

  /**
   * Test UrlItem null error condition. 
   */
  @Test
  public void testUrlItemWrongObj() {
    WebSpider.UrlItem urlItem = new WebSpider.UrlItem(testStr);
    Boolean result = urlItem.equals(new Object());
    assertFalse("Check compare with wrong object", result);
  }

  
  /**
   * Test list to check if most popular URL is calculated correctly.
   */
  @Test
  public void testUrlList() {
    WebSpider.UrlList urlList = new WebSpider.UrlList();
    urlList.add("Test1");
    urlList.add("Test2");
    urlList.add("Test2");
    WebSpider.UrlItem item = urlList.getMostPopular();
    assertEquals("Test most popular URL string.",item.url,"Test2");
    assertTrue("Test links pointing to most popular URL.",(item.count == 2));
  }
  
  /**
   * Test formatter for correct log record formating.
   */
  @Test
  public void testWebSpiderFormatter() {
    
    // Set up expected result and time stamp
    Date date = new Date();
    Long time = date.getTime();
    String expectedStr = date.toString();
    expectedStr = expectedStr.substring(10,19);
    expectedStr = expectedStr.concat(" Test Message\r\n");
    
    // Setup log record
    LogRecord record = new LogRecord(Level.INFO,"Test Message");
    record.setMillis(time);
    
    // Test formatter
    WebSpider.WebSpiderFormatter formatter = new WebSpider.WebSpiderFormatter();
    String result = formatter.format(record);
    assertEquals("Test formatter result.", expectedStr, result);
  }
}
